﻿---@diagnostic disable: trailing-space

-- 用于存放一些函数
-- 使用时在拓展开头加一句 local MoeFunc = require "packages.moepack.moepack_function"


local U = require "packages.utility.utility"

local MoeFunc = {}








-- 获取一张衍生牌的id，如果房间没开启则打印一张。
-- 如果没有加载此牌则返回空值
---@param room Room
---@param name string @ 需要的牌名
---@return integer|nil
MoeFunc.getDerivedCard = function(room, name)
  if Fk.all_card_types[name] == nil then return nil end
  local cards = room:getTag("MoeDerivedCard") or {}
  local cardId = table.find(cards, function (id)
    return Fk:getCardById(id, true).name == name
  end)
  if cardId then return cardId end
  cardId = table.find(Fk:getAllCardIds(), function (id)
    return Fk:getCardById(id, true).name == name
  end)
  if not cardId then
    local c = Fk.all_card_types[name]
    cardId = room:printCard(name, c.suit, c.number).id
  end
  if cardId then
    table.insert(cards, cardId)
    room:setTag("MoeDerivedCard", cards)
  end
  return cardId
end





-- 判断某角色是否能在某事件中使用某牌。
-- 用于某些被动使用的牌，例如无懈可击，金，诱敌深入，草船借箭
---@param player ServerPlayer
---@param name string @ 需要询问的牌名
---@param data CardEffectEvent|CardUseStruct
---@param cardResponsing? boolean @ 是否正在询问“打出”
---@return boolean
MoeFunc.canResponse = function (player, name, data, cardResponsing)
  local temp = Fk.currentResponsePattern
  Fk.currentResponsePattern = name
  local check = function()
    local room = player.room
    local cardCloned = Fk:cloneCard(name)
    if player:prohibitUse(cardCloned) then return false end
    if data.disresponsive or data.unoffsetable then return false end
    if table.contains(data.disresponsiveList or Util.DummyTable, player.id) or
    table.contains(data.unoffsetableList or Util.DummyTable, player.id) then return false end
    local cards = player:getHandlyIds()
    if table.find(cards, function (cid) return Fk:getCardById(cid).trueName == name end) then return true end
    Self = player -- for enabledAtResponse
    local allSkills = table.connect(player.player_skills, player._fake_skills)
    for _, t in pairs(player.derivative_skills) do
      for _, s in ipairs(t) do
        table.insertIfNeed(allSkills, s)
      end
    end
    for _, s in ipairs(allSkills) do 
      if s.pattern and Exppattern:Parse(name):matchExp(s.pattern) and
        not (s.enabledAtResponse and not s:enabledAtResponse(player, cardResponsing)) then
        return true
      end
    end
  end
  local yes = check ()
  Fk.currentResponsePattern = temp
  return yes
end


-- 从一个可能是表的数据dat中搜索值value
---@return boolean
local function findValue (dat, value)
  if dat == value then return true end
  if type(dat) == "table" then
    for _, v in pairs(dat) do
      if v == value then return true end
      if type(v) == "table" then
        if findValue(v, value) then
          return true
        end
      end
    end
  end
  return false
end


MoeFunc.findValue = findValue

-- 多名角色各摸一张牌，按座位行动
---@param players table<integer|ServerPlayer>
MoeFunc.allDraw = function(players, n, skillName)
  local room = RoomInstance
  if not room then return end
  local tos = {}
  for _, p in ipairs(players) do
    table.insert(tos, type(p) == "number" and p or p.id)
  end
  room:sortPlayersByAction(tos)
  for _, pid in ipairs(tos) do
    if not room:getPlayerById(pid).dead then
      room:getPlayerById(pid):drawCards(n, skillName)
    end
  end
end



-- 给房间添加技能
---@param skill Skill|string|table<Skill|string>
MoeFunc.addSkillToRoom = function(skill)
  local room = Fk:currentRoom()
  local skills = type(skill) ~= "table" and {skill} or skill
  for _, _skill in ipairs(skills) do
    if type(_skill) == "string" then _skill = Fk.skills[_skill] end
    if _skill then
      local toadd = table.connect({_skill}, _skill.related_skills)
      for _, s in ipairs(toadd) do
        if s:isInstanceOf(TriggerSkill) and RoomInstance then
          room.logic:addTriggerSkill(s)
        end
        if s:isInstanceOf(StatusSkill) then
          room.status_skills[s.class] = room.status_skills[s.class] or {}
          table.insertIfNeed(room.status_skills[s.class], s)
        end
      end
    end
  end
end

-- 判断一个技能是否没有技能标签
---@param skill Skill|string
---@return boolean
MoeFunc.isPureSkill = function (skill)
  skill = type(skill) == "string" and Fk.skills[skill] or skill
  local desc = Fk:translate(":"..skill.name, "zh_CN")
  desc = desc:gsub("<[^>]+>", "") -- 删除html标记，防止干扰判断
  local skill_tips = {"隐匿"}
  if string.sub(desc, 7, 9) == "技" or table.find(skill_tips, function(s) return desc:startsWith(s) end) then
    return false
  end
  return skill and not skill.lordSkill and skill.frequency < 3 and #skill.attachedKingdom == 0 and not skill.switchSkillName
  and not skill.permanent_skill
end


-- 获得数个【影】
---@param room Room
---@param n integer @ 获得的张数
---@return table<integer>
MoeFunc.getShade = function (room, n)
  if not Fk.all_card_types["shade"] then return {} end
  local ids = {}
  for _, id in ipairs(room.void) do
    if n <= 0 then break end
    local c = Fk:getCardById(id)
    if c.name == "shade" then
      if c:getMark(MarkEnum.DestructIntoDiscard) == 0 then
        room:setCardMark(c, MarkEnum.DestructIntoDiscard, 1)
      end
      table.insert(ids, id)
      n = n - 1
    end
  end
  while n > 0 do
    local card = room:printCard("shade", Card.Spade, 1)
    room:setCardMark(card, MarkEnum.DestructIntoDiscard, 1)
    table.insert(ids, card.id)
    n = n - 1
  end
  return ids
end



-- 无上限表，不大于上限时（默认4）显示所有内容，大于上限时显示最新内容，点开qml显示全部内容
-- mark: @[:]skillname
Fk:addQmlMark{
  name = "list",
  how_to_show = function(name, value)
    if type(value) == "table" and #value > 0 then
      local max = 4
      local str = table.concat(table.slice(value, math.max(1, #value-max+1), #value+1), ",")
      if #value > 4 then
        str = str.."..."
      end
      return str
    end
    return " "
  end,
  qml_path = "packages/moepack/qml/ListBox"
}

-- 播放某角色触发了延迟效果
---@param player ServerPlayer
---@param skillName string
---@param skill_type string|nil @ 技能特效
MoeFunc.broadcastSkillDelay = function (player, skillName, skill_type)
  player:broadcastSkillInvoke(skillName)
  player.room:doAnimate("InvokeSkill", {
    name = skillName,
    player = player.id,
    skill_type = skill_type or "special",
  })
  player.room:sendLog{ type = "#MoeSkillDelay", from = player.id, arg = skillName }
end

Fk:loadTranslationTable{
  ["#MoeSkillDelay"] = "%from 触发了“%arg”的延迟效果",
}


-- 切换皮肤
---@param player ServerPlayer
---@param skinList table<string> @ 需要切换的武将名列表
MoeFunc.changeSkin = function (player, skinList)
  local list = table.filter(skinList, function(g) return Fk.generals[g] end)
  if #list < 2 then return end
  local room = player.room
  local findNext = function (g)
    local i = table.indexOf(list, g)
    if i ~= -1 then
      i = (i == #list) and 1 or (i + 1)
    end
    return i
  end
  local index = findNext(player.general)
  if index ~= -1 then
    room:setPlayerProperty(player, "general", list[index])
  end
  index = findNext(player.deputyGeneral)
  if index ~= -1 then
    room:setPlayerProperty(player, "deputyGeneral", list[index])
  end
end




-- 交换两名角色的势力、武将、所有技能
-- （技能使用次数也会被交换，防止限定技觉醒技二用）
---@param targetOne ServerPlayer @ 第一个角色
---@param TargetTwo ServerPlayer @ 第二个角色
MoeFunc.swapGeneralAndSkills = function (targetOne, TargetTwo)
  local room = targetOne.room
  local tos = {targetOne, TargetTwo}
  -- 记录势力
  local kingdom_data = {targetOne.kingdom, TargetTwo.kingdom}
  -- 记录武将
  local general_data = {}
  for i, p in ipairs(tos) do
    table.insert(general_data, {p.general, p.deputyGeneral})
  end
  -- 记录并删除技能
  local skill_data = {{},{}}
  local getSkills = function (p)
    return table.filter(p.player_skills, function (s)
      return not (s:isEquipmentSkill(p) or s.name:endsWith("&"))
    end)
  end
  local history_list = {Player.HistoryPhase,Player.HistoryTurn,Player.HistoryRound,Player.HistoryGame}
  for i, p in ipairs(tos) do
    local skills = table.map(getSkills(p), Util.NameMapper)
    if #skills > 0 then
      for _, s in ipairs(skills) do
        -- 为了处理限定技觉醒技等技能，需要记录技能发动次数
        local h = {}
        for _, v in ipairs(history_list) do
          table.insert(h, p:usedSkillTimes(s, v))
        end
        table.insert(skill_data[i], {s, h})
      end
      room:handleAddLoseSkills(p, "-"..table.concat(skills, "|-"), nil, false)
    end
  end
  -- 变更势力
  for i, p in ipairs(tos) do
    p.kingdom = kingdom_data[(i%2)+1]
    room:broadcastProperty(p, "kingdom")
  end
  -- 变更武将
  for i, p in ipairs(tos) do
    local ge, de = table.unpack(general_data[(i%2)+1])
    p.general = ge
    p.gender = Fk.generals[ge].gender
    p.deputyGeneral = de
    room:broadcastProperty(p, "general")
    room:broadcastProperty(p, "gender")
    room:broadcastProperty(p, "deputyGeneral")
  end
  -- 获得对方的技能
  for i, p in ipairs(tos) do
    for _, s_data in ipairs(skill_data[(i%2)+1]) do
      local s, h = table.unpack(s_data)
      room:handleAddLoseSkills(p, s, nil, false)
      for j, times in ipairs(h) do
        p:setSkillUseHistory(s, times, history_list[j])
      end
    end
  end
end



-- 获得一名角色的所有技能，不含隐藏、装备、附加(黄天给牌)、衍生(豹变)技能
---@param player ServerPlayer @ 角色
---@param name? boolean @ 是否返回技能名，默认不
---@return Skill[] | string[]
MoeFunc.getPlayerSkills = function (player, name)
  local ret = table.filter(player.player_skills, function (s)
    return s.visible and s:isPlayerSkill(player)
  end)
  return name and table.map(ret, Util.NameMapper) or ret
end


-- 令一张使用的牌无效，若为装备牌或延时锦囊，取消目标
---@param data CardUseStruct
MoeFunc.nullifyUseCard = function (data)
  if data.toCard then
    data.toCard = nil
  else
    data.nullifiedTargets = table.map(Fk:currentRoom().players, Util.IdMapper)
    if data.card.type == Card.TypeEquip or data.card.sub_type == Card.SubtypeDelayedTrick then
      data.tos = {}
    end
  end
end



return MoeFunc
